home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’95
/
What's On My Mac
/
cgi.c
< prev
next >
Wrap
Text File
|
1995-06-24
|
21KB
|
803 lines
/*****
*
* Grant's CGI Shell (Common Grant Interface :-)
*
* cgi.c
*
* Standard functions for cgi applications.
* You must call InitCGIUtil in your application startup.
* You must install CGIAEHandle as the event handler for the WWWΩsdoc apple event
* You must write the function:
* void MyCGIProcess ( CGIHdl theCGIHdl )
*
* by Grant Neufeld (with Scott T. Boyd and bits of other help from other people)
* the original source that this was inspired by is "Responder" written by John O'Fallon
* see his site: http://www.maxum.com/maxum/
*
* watch my url for future upgrades
*
* if you want notice of upgrades, subscribe to macwwwtool@arpp1.carelton.ca
*
* http://arpp1.carleton.ca/grant/
* gneufeld@ccs.carleton.ca
* grant@acm.org
*
* Copyright ©1995 by Grant Neufeld
*
* This source may be freely used as long as the copyright notice is kept in the source.
* I (we) ask that you let us know of any enhancements (read: bug fixes) to this code.
* I (we) would also like copies of (or discounts on) anything you produce this with, please.
*
*****/
#include <stdlib.h>
#include <string.h>
#include "DebugUtil.h"
#include "MemoryUtil.h"
#include "StringUtil.h"
#include "cgi.h"
/*** CONSTANT DECLARATIONS ***/
#define kHTTPHeaderStrs 129
#define kHTTPHeaderOK 1
#define kHTTPHeaderRedirect 2
#define kHTTPHeaderErr 3
/*** VARIABLE DECLARATIONS ***/
/*** EXTERNAL FUNCTION PROTOTYPES ***/
void MyCGIProcess ( CGIHdl );
/*** LOCAL FUNCTION PROTOTYPES ***/
OSErr cgiAEGetParamString ( AppleEvent *, AEKeyword, char **, char *, long );
OSErr cgiAEGetParamShort ( AppleEvent *, AEKeyword, short *, char *, long );
OSErr cgiAEGetParamHTTPMethod ( AppleEvent *, AEKeyword, HTTPMethod *, char *, long );
/*** FUNCTIONS ***/
/* */
void
InitCGIUtil ( void )
{
GetIndString ( gHTTPHeaderOK, kHTTPHeaderStrs, kHTTPHeaderOK );
P2CStr ( gHTTPHeaderOK );
gHTTPHeaderOKSize = strlen ( (char *)gHTTPHeaderOK );
GetIndString ( gHTTPHeaderRedirect, kHTTPHeaderStrs, kHTTPHeaderRedirect );
P2CStr ( gHTTPHeaderRedirect );
gHTTPHeaderRedirectSize = strlen ( (char *)gHTTPHeaderRedirect );
GetIndString ( gHTTPHeaderErr, kHTTPHeaderStrs, kHTTPHeaderErr );
P2CStr ( gHTTPHeaderErr );
gHTTPHeaderErrSize = strlen ( (char *)gHTTPHeaderErr );
} /* InitCGIUtil */
#pragma mark -
/* */
void
CGIDisposeHandle ( CGIHdl theCGIHdl )
{
if ( (*theCGIHdl)->path_args != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->path_args) );
}
if ( (*theCGIHdl)->http_search_args != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->http_search_args) );
}
if ( (*theCGIHdl)->username != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->username) );
}
if ( (*theCGIHdl)->password != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->password) );
}
if ( (*theCGIHdl)->from_user != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->from_user) );
}
if ( (*theCGIHdl)->client_address != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->client_address) );
}
if ( (*theCGIHdl)->post_args != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->post_args) );
}
if ( (*theCGIHdl)->server_name != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->server_name) );
}
if ( (*theCGIHdl)->script_name != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->script_name) );
}
if ( (*theCGIHdl)->content_type != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->content_type) );
}
if ( (*theCGIHdl)->referer != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->referer) );
}
if ( (*theCGIHdl)->user_agent != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->user_agent) );
}
if ( (*theCGIHdl)->action != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->action) );
}
if ( (*theCGIHdl)->action_path != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->action_path) );
}
if ( (*theCGIHdl)->client_ip != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->client_ip) );
}
if ( (*theCGIHdl)->full_request != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->full_request) );
}
if ( (*theCGIHdl)->responseData != nil )
{
DisposePtr ( (Ptr)((*theCGIHdl)->responseData) );
}
if ( (*theCGIHdl)->formFields != nil )
{
CGIFormFieldsDispose ( (*theCGIHdl)->formFields );
}
} /* CGIDisposeHandle */
#pragma mark -
/* The separator '&' separates individual fields.
The delimiter '=' delimits the name and value in a field.
Returns an array of field records with the last one containing null values. */
CGIFormField *
CGIFormFieldsFromArgs ( char *theString, long *count )
{
CGIFormField * theFields;
long totalStrSize;
long totalFields;
long nameSize;
long valueSize;
long currentField;
char * theStringPtr;
char * fieldSeparator;
char * fieldDelimiter;
my_assert ( theString != nil, "CGIFormArgs: nil string" );
theFields = nil;
/* don't return number of fields until function is successful */
*count = nil;
totalStrSize = strlen ( theString );
totalFields = StringCountChar ( theString, kCGIFormFieldSeparator ) + 1;
theFields = (CGIFormField *) MyNewPtr ( ((totalFields + 1) * sizeof(CGIFormField)), nil );
if ( theFields == nil )
{
goto Exit_Fail;
}
(theFields[totalFields]).name = nil;
(theFields[totalFields]).value = nil;
theStringPtr = theString;
for ( currentField = nil; currentField < totalFields; currentField++ )
{
fieldDelimiter = strchr ( theStringPtr, kCGIFormFieldDelimiter );
fieldSeparator = strchr ( theStringPtr, kCGIFormFieldSeparator );
/* if there is a field delimiter, and it is before any field separator */
if ( (fieldDelimiter != nil) && ((fieldSeparator > fieldDelimiter) || (fieldSeparator == nil)) )
{
/* field name */
/* the size of the name string is the difference between the begining of the
field and the position of the field delimiter */
nameSize = fieldDelimiter - theStringPtr;
/* allocate the name string */
(theFields[currentField]).name = (char *) MyNewPtr ( nameSize + 1, nil );
if ( (theFields[currentField]).name == nil )
{
(theFields[currentField]).value = nil;
goto Exit_Fail;
}
/* copy the field name */
BlockMove ( theStringPtr, (theFields[currentField]).name, nameSize );
/* null terminate the end of the name string */
((theFields[currentField]).name)[nameSize] = nil;
/* convert the url encoded text to a normal string */
CGIDecodeSpecialChars ( (theFields[currentField]).name );
/* field value */
if ( fieldSeparator != nil )
{
valueSize = fieldSeparator - (fieldDelimiter + 1);
}
else
{
valueSize = strlen ( fieldDelimiter + 1 );
}
(theFields[currentField]).value = (char *) MyNewPtr ( (valueSize + 1), nil );
if ( (theFields[currentField]).value == nil )
{
DisposePtr ( (theFields[currentField]).name );
(theFields[currentField]).name = nil;
goto Exit_Fail;
}
BlockMove ( fieldDelimiter + 1, (theFields[currentField]).value, valueSize );
((theFields[currentField]).value)[valueSize] = nil;
CGIDecodeSpecialChars ( (theFields[currentField]).value );
theStringPtr = fieldSeparator + 1;
}
else
{
/* invalid data encountered, abort function */
goto Exit_Fail;
}
}
*count = totalFields;
return theFields;
Exit_Fail:
if ( theFields != nil )
{
/* release allocated memory */
// CGIFormFieldsDispose ( theFields );
}
return nil;
} /* CGIFormFieldsFromArgs */
/* */
CGIFormField *
CGIFormFieldsFindRecord ( CGIFormField *fieldArray, char *fieldName )
{
long currentField;
/* look til we find something or we hit the end */
for ( currentField = nil; (fieldArray[currentField]).name != nil; currentField++ )
{
if ( strcmp((fieldArray[currentField]).name, fieldName) == nil )
{
/* found a match */
return &(fieldArray[currentField]);
}
}
/* didn't find a match */
return nil;
} /* CGIFormFieldsFindRecord */
/* Deallocate memory for theFields array */
void
CGIFormFieldsDispose ( CGIFormField *theFields )
{
long offset;
my_assert ( theFields != nil, "CGIFormFieldsDispose: null field array pointer" );
offset = nil;
while ( true )
{
if ( (theFields[offset]).name == nil )
{
/* terminating field reached */
DisposePtr ( (Ptr)theFields );
return;
}
DisposePtr ( (Ptr)((theFields[offset]).name) );
if ( (theFields[offset]).value == nil )
{
DisposePtr ( (Ptr)((theFields[offset]).value) );
}
offset++;
}
} /* CGIFormFieldsDispose */
#pragma mark -
/* replaces instances of percent signs (%) followed by an ASCII char value
with the actual character */
void
CGIDecodeSpecialChars ( char *theString )
{
int read;
int write;
int AsciiChar;
my_assert ( theString != nil, "CGIDecodeSpecialChars: nil string" );
read = nil;
write = nil;
while ( theString[read] != nil )
{
// Search the entire text block
switch ( theString[read] )
{
case '%':
// if the char is a percent sign, look at the next char
if ( (theString[read+1] >= 'A') && (theString[read+1] <= 'F') )
{
// convert A thru F to 10 thru 15
AsciiChar = theString[read+1] - 'A' + 10;
}
else if ( (theString[read+1] >= 'a') && (theString[read+1] <= 'f') )
{
// convert a thru f to 10 thru 15
AsciiChar = theString[read+1] - 'a' + 10;
}
else
{
// Convert all others to 0 to 9
AsciiChar = theString[read+1] - '0';
}
// That was the high order of a hex number, so mult by 16 to find the decimal value.
AsciiChar = AsciiChar * 16;
// Now do the low order char...
if ( (theString[read+2] >= 'A') && (theString[read+2] <= 'F') )
{
// Convert A thru F to 10 thru 15 (and add it to the high order value)
AsciiChar += (theString[read+2] - 'A' + 10);
}
else if ( (theString[read+2] >= 'a') && (theString[read+2] <= 'f') )
{
// Convert a thru f to 10 thru 15 (and add it to the high order value)
AsciiChar += (theString[read+2] - 'a' + 10);
}
else
{
// Convert all others to 0 to 9 (and add it to the high order value)
AsciiChar += (theString[read+2] - '0');
}
if ((AsciiChar >= 0) && (AsciiChar < 256))
{
// if we got a valid value, replace the ASCII char
if ( AsciiChar == 10 )
{
/* don't write newline */
write--;
}
else
{
// Copy the char into the string
theString[write] = AsciiChar;
}
// Now that we have converted them, skip the extra characters
read += 2;
}
else
{
// False alarm, nothing to convert, just copy the char (shouldn't happen)
theString[write] = theString[read];
}
break;
case '+':
// Spaces are coded as plus signs
theString[write] = ' ';
break;
case 10:
// Strip out linefeeds, Macs don't use them
write--;
break;
default:
// A normal character, just copy it
theString[write] = theString[read];
break;
}
read++;
write++;
}
// make sure the string is capped
theString[write] = '\0';
} /* CGIDecodeSpecialChars */
#define kCGIEncodeExtraChars 16
/* %hex encode all chars > 127 */
char *
CGIEncodeSpecialChars ( char *theString )
{
char * theResult;
long strSize;
long tempSize;
long strOffset;
long resOffset;
strSize = strlen ( theString );
tempSize = strSize + kCGIEncodeExtraChars;
theResult = (char *) MyNewPtr ( (tempSize + 1), nil );
for ( strOffset = resOffset = nil; strOffset < strSize; strOffset++, resOffset++ )
{
if ( resOffset > tempSize )
{
tempSize += kCGIEncodeExtraChars;
SetPtrSize ( (Ptr)theResult, tempSize + 1 );
}
if ( theString[strOffset] < 128 )
{
/* if the character is normal ascii, not high-bit, just copy it */
theResult[resOffset] = theString[strOffset];
}
else
{
if ( (resOffset + 2) > tempSize )
{
tempSize += kCGIEncodeExtraChars;
SetPtrSize ( (Ptr)theResult, tempSize + 1 );
}
/* character is high-bit, encode it */
CGICharToHex ( theString[strOffset], &(theResult[resOffset]) );
/* add the extra two characters for hex encoding to the result offset */
resOffset += 2;
}
}
if ( resOffset != tempSize )
{
SetPtrSize ( (Ptr)theResult, resOffset + 1 );
}
return theResult;
} /* CGIEncodeSpecialChars */
/* */
void
CGICharToHex ( unsigned char theChar, char *theString )
{
unsigned char tmpChr;
theString[0] = '%';
/* high 4 bits */
tmpChr = ( theChar & 0xF0 ) >> 4;
theString[1] = ( tmpChr > 9 )?
( tmpChr - 10 ) +'A' :
tmpChr + '0';
/* low 4 bits */
tmpChr = theChar & 0x0F;
theString[2] = ( tmpChr > 9 )?
( tmpChr - 10 ) + 'A' :
tmpChr + '0';
} /* CGICharToHex */
#pragma mark -
#pragma segment AppleEvent
/* AppleEvent Handler for the CGI WWWΩ-sdoc event */
pascal OSErr
CGIAEHandle ( AppleEvent *theAppleEvent, AppleEvent *TheReply, long Reference )
{
OSErr theErr;
DescType actualType;
Ptr tempBuffer;
CGIHdl theCGIHdl;
theCGIHdl = ( CGIHdl ) MyNewHandleClear ( sizeof(CGIrecord), &theErr );
if ( theCGIHdl == nil )
{
return theErr;
}
/* store references to the apple event and reply records */
(*theCGIHdl)->appleEvent = theAppleEvent;
(*theCGIHdl)->replyEvent = TheReply;
tempBuffer = MyNewPtr ( kCGIParamMaxSize, &theErr );
if ( tempBuffer == nil )
{
DisposeHandle ( (Handle)theCGIHdl );
return theErr;
}
actualType = ( DescType ) 'char';
HLockHi ( (Handle)theCGIHdl );
/* '----' - direct parameter:
path_args - arguments to the URL after a $ */
theErr = cgiAEGetParamString ( theAppleEvent, '----', &((*theCGIHdl)->path_args),
(char *)tempBuffer, kCGIParamMaxSize );
if ( theErr == noErr )
{
CGIDecodeSpecialChars ( (*theCGIHdl)->path_args );
}
/* 'kfor' - search arguments:
http_search_args - arguments to the URL after a ? */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIhttp_search_args, &((*theCGIHdl)->http_search_args),
(char *)tempBuffer, kCGIParamMaxSize );
/* leave decoding to after parsing of form fields */
/* 'user' - user name:
username - authenticated user name */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIusername, &((*theCGIHdl)->username),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'pass' - password:
password - authenticated password */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIpassword, &((*theCGIHdl)->password),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'frmu' - from user:
from_user - non-standard. e-mail address of remote user */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIfrom_user, &((*theCGIHdl)->from_user),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'addr' - client address:
client_address - IP address or domain name of remote client's host */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIclient_address, &((*theCGIHdl)->client_address),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'post' - post arguments:
post_args - */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIpost_args, &((*theCGIHdl)->post_args),
(char *)tempBuffer, kCGIParamMaxSize );
/* leave decoding to after parsing of form fields */
/* 'meth' - HTTP method:
method - GET, POST, etc. Used to tell if post_args are valid */
theErr = cgiAEGetParamHTTPMethod ( theAppleEvent, kCGImethod, &((*theCGIHdl)->method),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'svnm' - server name:
server_name - name or IP address of this server */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIserver_name, &((*theCGIHdl)->server_name),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'svpt' - server port:
server_port - TCP/IP port number being used by this server */
theErr = cgiAEGetParamShort ( theAppleEvent, kCGIserver_port, &((*theCGIHdl)->server_port),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'scnm' - script name:
script_name - URL name of this script */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIscript_name, &((*theCGIHdl)->script_name),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'ctyp' - content type:
content_type - MIME content type of post_args */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIcontent_type, &((*theCGIHdl)->content_type),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'refr' - referer:
referer - the URL of the page referencing this document */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIreferer, &((*theCGIHdl)->referer),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'Agnt' - user agent:
user_agent - the name and version of the WWW client software being used */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIuser_agent, &((*theCGIHdl)->user_agent),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'Kact' - action name:
action - the name of the action (CGI or ACGI if not a user defined action) */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIaction, &((*theCGIHdl)->action),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'Kapt' - action path:
action_path - path to the action application */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIaction_path, &((*theCGIHdl)->action_path),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'Kcip' - client IP address:
client_ip - the IP address of the client */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIclient_ip, &((*theCGIHdl)->client_ip),
(char *)tempBuffer, kCGIParamMaxSize );
/* 'Kfrq' - full request:
full_request - the full text of the request */
theErr = cgiAEGetParamString ( theAppleEvent, kCGIfull_request, &((*theCGIHdl)->full_request),
(char *)tempBuffer, kCGIParamMaxSize );
/* separate the form fields into an array */
switch ( (*theCGIHdl)->method )
{
case HTTP_get :
if ( (*theCGIHdl)->http_search_args != nil )
{
(*theCGIHdl)->formFields = CGIFormFieldsFromArgs ( (*theCGIHdl)->http_search_args, &((*theCGIHdl)->totalFields) );
}
break;
case HTTP_post :
default :
if ( (*theCGIHdl)->post_args != nil )
{
(*theCGIHdl)->formFields = CGIFormFieldsFromArgs ( (*theCGIHdl)->post_args, &((*theCGIHdl)->totalFields) );
}
break;
}
/* now that the need to use them for form fields is over, we can
decode the search args */
CGIDecodeSpecialChars ( (*theCGIHdl)->http_search_args );
/* this is where the application specific cgi handling comes into play */
HUnlock ( (Handle)theCGIHdl );
MyCGIProcess ( theCGIHdl );
HLock ( (Handle)theCGIHdl );
if ( (*theCGIHdl)->responseData != nil )
{
theErr = AEPutParamPtr ( TheReply, keyDirectObject, typeChar,
(Ptr)((*theCGIHdl)->responseData), (*theCGIHdl)->responseSize );
}
else
{
theErr = AEPutParamPtr ( TheReply, keyDirectObject, typeChar, (Ptr)gHTTPHeaderErr, gHTTPHeaderErrSize );
}
HUnlock ( (Handle)theCGIHdl );
/* deallocate memory */
CGIDisposeHandle ( theCGIHdl );
return theErr;
} /* CGIAEHandle */
#pragma segment AppleEvent
/* */
OSErr
cgiAEGetParamString ( AppleEvent *theAppleEvent, AEKeyword theAEKeyword, char **theString, char *tempBuffer, long bufferSize )
{
OSErr theErr;
DescType actualType;
Size actualSize;
my_assert ( *theString == nil, "cgiAEGetParamString: non-nil string received" );
theErr = AEGetParamPtr
( theAppleEvent, theAEKeyword, typeChar, &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
if ( theErr == noErr )
{
*theString = (char *) MyNewPtr ( actualSize + 1, &theErr );
if ( *theString != nil )
{
/* copy the tempBuffer into the CGI handle */
BlockMove ( tempBuffer, *theString, actualSize );
/* put a null terminator at the end of the string */
(*theString)[actualSize] = nil;
}
}
return theErr;
} /* cgiAEGetParamString */
#pragma segment AppleEvent
/* */
OSErr
cgiAEGetParamShort ( AppleEvent *theAppleEvent, AEKeyword theAEKeyword, short *theShort, char *tempBuffer, long bufferSize )
{
OSErr theErr;
DescType actualType;
Size actualSize;
theErr = AEGetParamPtr
( theAppleEvent, theAEKeyword, typeChar, &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
if ( theErr == noErr )
{
/* terminate the buffer with a null byte */
tempBuffer[actualSize] = nil;
*theShort = atoi ( tempBuffer );
}
return theErr;
} /* cgiAEGetParamShort */
#pragma segment AppleEvent
/* */
OSErr
cgiAEGetParamHTTPMethod ( AppleEvent *theAppleEvent, AEKeyword theAEKeyword, HTTPMethod *theMethod, char *tempBuffer, long bufferSize )
{
OSErr theErr;
DescType actualType;
Size actualSize;
int stringDiff;
theErr = AEGetParamPtr
( theAppleEvent, theAEKeyword, typeChar, &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
if ( theErr == noErr )
{
/* terminate the buffer with a null byte */
tempBuffer[actualSize] = nil;
/* compare the buffer with constants to determine the http method used */
stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGet );
if ( stringDiff == nil )
{
*theMethod = HTTP_get;
}
else
{
stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodPost );
if ( stringDiff == nil )
{
*theMethod = HTTP_post;
}
else
{
*theMethod = HTTP_UNDEFINED;
}
}
}
return theErr;
} /* cgiAEGetParamHTTPMethod */
#pragma segment Main
/*** EOF ***/